<?php
declare(strict_types=1);

namespace Dreamcat\ConfigClient\Impl;

use Dreamcat\ConfigClient\Api\NacosServerInterface;
use Dreamcat\ConfigClient\ConfigCenterListener;
use Dreamcat\ConfigClient\Consts\NacosConst;
use Dreamcat\ConfigClient\Popo\ConfigResult;
use Dreamcat\ConfigClient\Popo\ConfigVersion;
use Dreamcat\ConfigClient\Popo\NacosAppInfo;
use Dreamcat\ConfigClient\Popo\NacosNotifiParam;
use RuntimeException;

/**
 * nacos配置中心监听
 * @author vijay
 */
class NacosConfigListener implements ConfigCenterListener
{
    /** @var NacosServerInterface nacos服务器 */
    private $nacosServer;
    /** @var NacosNotifiParam[] 监听的列表 */
    private $listenApps;
    /** @var array 监听的列表，以dataId、group、namespace为键的配置 */
    private $map = [];

    /**
     * NacosConfigListener constructor.
     * @param NacosServerInterface $nacosServer nacos服务器
     * @param NacosAppInfo[] $listenApps 要监听的应用列表
     */
    public function __construct(NacosServerInterface $nacosServer, array $listenApps)
    {
        $this->nacosServer = $nacosServer;
        $this->listenApps = array_map(
            function (NacosAppInfo $appInfo) {
                $param = new NacosNotifiParam();
                $param->setDataId($appInfo->getDataId())
                    ->setGroup($appInfo->getGroup())
                    ->setNamespaceName($appInfo->getNamespaceName());
                return $param;
            },
            $listenApps
        );
        foreach ($this->listenApps as $param) {
            $this->map[$param->getDataId()][$param->getGroup()][$param->getNamespaceName()] = $param;
        }
    }

    /**
     * @inheritDoc
     */
    public function notifications(array $lastVersions = []): array
    {
        foreach ($lastVersions as $lastVersion) {
            $this->version2NotifiParam($lastVersion);
        }
        $changes = $this->nacosServer->notifications(array_values($this->listenApps));
        return array_map(
            [
                $this,
                "nacosAppInfo2Version",
            ],
            $changes
        );
    }

    /**
     * 将nacos应用信息转为标准格式
     * @param NacosAppInfo $param
     * @return mixed 配置信息
     */
    public function nacosAppInfo2Version(NacosAppInfo $param)
    {
        return [
            "dataId" => $param->getDataId(),
            "group" => $param->getGroup(),
            "namespaceName" => $param->getNamespaceName(),
        ];
    }

    /**
     * 将ConfigVersion 转为 NacosNotifiParam
     * @param ConfigVersion $configVersion
     */
    public function version2NotifiParam(ConfigVersion $configVersion): void
    {
        [
            $dataId,
            $group,
            $namespace,
        ] = $this->parseAppInfo($configVersion->getAppInfo());
        if (!isset($this->map[$dataId][$group][$namespace])) {
            return;
        }
        /** @var NacosNotifiParam $param */
        $param = $this->map[$dataId][$group][$namespace];
        $param->setReleaseKey($configVersion->getReleaseKey());
    }

    /**
     * @inheritDoc
     */
    public function getConfig(ConfigVersion $configVersion): ConfigResult
    {
        [
            $dataId,
            $group,
            $namespace,
        ] = $this->parseAppInfo($configVersion->getAppInfo());
        $result = $this->nacosServer->getConfig($dataId, $group, $namespace);
        $configResult = new ConfigResult();
        $configResult->setSuccess($result->getSuccess());
        if (!$result->getSuccess()) {
            return $configResult;
        }
        $configResult->setAppInfo($configVersion->getAppInfo())
            ->setReleaseKey($result->getReleaseKey())
            ->setConfigurations($result->getConfigurations());
        return $configResult;
    }

    /**
     * 解析应用信息
     * @param mixed $app
     * @return array 依次是 dataId group namespace
     */
    protected function parseAppInfo($app): array
    {
        if (!is_array($app)) {
            throw new RuntimeException("nacos的应用信息必须放在数组中");
        }
        if (!isset($app["dataId"])) {
            throw new RuntimeException("nacos配置获取必须有dataId");
        }
        $dataId = $app["dataId"];
        $group = $app["group"] ?? NacosConst::DEFAULT_GROUP;
        $namespace = $app["namespaceName"] ?? NacosConst::DEFAULT_NAMESPACE;
        return [
            $dataId,
            $group,
            $namespace,
        ];
    }
}

# end of file
